﻿using System;
using System.Collections.Generic;
using System.Text;
using Stauffware.Common.Core;
using NUnit.Framework;
using NU=NUnit.Framework;

namespace UnitTests.Core.ErrorOr
{
	class ErrorOr_UnitTests
	{
		// for testing ErrorOr where T is a value type
		static ErrorOr<int> DivideI (int a, int b)
		{
			if (b == 0)
				return new DivideByZeroException();
			else return a / b;
		}

		// for testing ErrorOr where T is a reference type
		static ErrorOr<string> DivideS (string a, string b)
		{
			Int32Parse pa = IntParser.Parse(a);
			if (pa.IsError)
				return pa.ToException();

			Int32Parse pb = IntParser.Parse(b);
			if (pb.IsError)
				return pb.ToException();

			ErrorOr<int> quotient = DivideI(pa.Value, pb.Value);
			if (quotient.IsError)
				return quotient.Exception;

			return quotient.Value.ToString();
		}

		private static readonly Int32Parser IntParser = 
			new Int32Parser(NumberParseOptions.ParseEntireInput);

		//-----------------------------------------------------------------------------

		[TestCase(3, -2, null)]
		[TestCase(1, 0, typeof(DivideByZeroException))]

		public static void DivideI (int a, int b, Type expectedExceptionType)
		{
			ErrorOr<int> actual = DivideI(a, b);

			if (expectedExceptionType == null)
			{
				int expectedValue = a / b;
				NU.Assert.IsFalse(actual.IsError, "IsError");
				NU.Assert.AreEqual(expectedValue, actual.Value, "Value");
			}
			else
			{
				NU.Assert.IsTrue(actual.IsError, "IsError");
				NU.Assert.NotNull(actual.Exception, "Exception");
				NU.Assert.AreEqual(expectedExceptionType, actual.Exception.GetType(), "typeof(Exception)");
			}
		}

		//-----------------------------------------------------------------------------

		[TestCase("12", "4", null)]
		[TestCase("9", "0", typeof(DivideByZeroException))]
		[TestCase("foo", "17", typeof(ParseException))]
		[TestCase("23", "bar", typeof(ParseException))]
		[TestCase("99999999999999", "-1", typeof(ParseException))]

		public static void DivideS (string a, string b, Type expectedExceptionType)
		{
			ErrorOr<string> actual = DivideS(a, b);
			System.Console.WriteLine(actual.ToString());

			if (expectedExceptionType == null)
			{
				string expectedValue = (int.Parse(a) / int.Parse(b)).ToString();
				NU.Assert.IsFalse(actual.IsError, "IsError");
				NU.Assert.AreEqual(expectedValue, actual.Value, "Value");
			}
			else
			{
				NU.Assert.IsTrue(actual.IsError, "IsError");
				NU.Assert.NotNull(actual.Exception, "Exception");
				NU.Assert.AreEqual(expectedExceptionType, actual.Exception.GetType(), "typeof(Exception)");
			}
		}

		//-----------------------------------------------------------------------------

		[TestCase]
		public static void ToString_String_Default ( )
		{
			ErrorOr<string> eror = new ErrorOr<string>();
			NU.Assert.AreEqual("<null>", eror.ToString());
		}

		[TestCase]
		public static void ToString_String_Normal ( )
		{
			ErrorOr<string> eror = new ErrorOr<string>("Hello, world!");
			NU.Assert.AreEqual("\"Hello, world!\"", eror.ToString());
		}

		[TestCase]
		public static void ToString_String_Error ( )
		{
			ErrorOr<string> eror = new ErrorOr<string>(new Exception("Goodbye, cruel world!"));
			NU.Assert.AreEqual("Goodbye, cruel world!", eror.ToString());
		}

		[TestCase]
		public static void ToString_Int_Default ( )
		{
			ErrorOr<int> eror = new ErrorOr<int>();
			NU.Assert.AreEqual("0", eror.ToString());
		}

		[TestCase]
		public static void ToString_Int_Normal ( )
		{
			ErrorOr<int> eror = new ErrorOr<int>(12345);
			NU.Assert.AreEqual("12345 (0x3039)", eror.ToString());
		}

		//-----------------------------------------------------------------------------
	}
}
